package com.ldgame.game;

import java.util.ArrayList;
import java.util.Iterator;

import org.lwjgl.input.Keyboard;
import org.lwjgl.util.vector.Vector2f;
import org.newdawn.slick.Color;

import com.ldgame.ai.AI;
import com.ldgame.ai.StandardAI;
import com.ldgame.entities.Building;
import com.ldgame.entities.Bullet;
import com.ldgame.entities.City;
import com.ldgame.entities.Entity;
import com.ldgame.entities.Player;
import com.ldgame.entities.Soldier;
import com.ldgame.entities.Team;
import com.ldgame.gfx.gfxelements.Sprite;
import com.ldgame.input.Input;
import com.ldgame.sound.Sound;
import com.ldgame.sound.SoundSystem;
import com.ldgame.utils.Timer;

public class RunningState implements GameState {
	private Game game;
	private Player player = new Player();
	
	private Sprite minimapBG = new Sprite(new Vector2f(736.0f, 536.0f),
										  128.0f,
										  128.0f,
										  new Vector2f(0.0f, 64.0f),
										  new Vector2f(8.0f, 8.0f),
										  new Color(0.6f, 0.5f, 0.3f, 1.0f));
	private Sprite minimapToken = new Sprite(new Vector2f(),
			  							  	8.0f,
			  							  	8.0f,
			  							  	new Vector2f(0.0f, 64.0f),
			  							  	new Vector2f(8.0f, 8.0f),
			  							  	new Color(0.5f, 0.5f, 0.5f, 1.0f));
	private Sprite selectionSprite = new Sprite(new Vector2f(),
			  									8.0f,
			  									8.0f,
			  									new Vector2f(0.0f, 64.0f),
			  									new Vector2f(8.0f, 8.0f),
			  									new Color(0.0f, 1.0f, 0.0f, 0.3f));
	private Sprite floorTile = new Sprite(new Vector2f(),
										  64.0f,
										  64.0f,
										  new Vector2f(0.0f, 0.0f),
										  new Vector2f(16.0f, 16.0f),
										  new Color(1.0f, 1.0f, 1.0f, 1.0f));
	private Sprite shadowSprite = new Sprite(new Vector2f(),
									       16.0f,
									       16.0f,
									       new Vector2f(0.0f, 40.0f),
									       new Vector2f(8.0f, 8.0f),
									       new Color(1.0f, 0.0f, 0.0f, 1.0f),
									       4,
									       0.125f);
	private Sprite healthSprite = new Sprite(new Vector2f(),
									       64.0f,
									       4.0f,
									       new Vector2f(0.0f, 64.0f),
									       new Vector2f(8.0f, 8.0f),
									       new Color(1.0f, 0.0f, 0.0f, 1.0f));
	private Sprite terrainDetail = new Sprite(new Vector2f(),
										       64.0f,
										       64.0f,
										       new Vector2f(16.0f, 16.0f),
										       new Vector2f(16.0f, 16.0f),
										       new Color(1.0f, 1.0f, 1.0f, 1.0f));
	private int [] detailState = new int[100];
	private Vector2f [] detailPosition = new Vector2f[100];
	
	Entity [] entities;
	ArrayList<Bullet> bullets = new ArrayList<Bullet>();
	City [] cities;
	
	public RunningState(Game game) {
		this.game = game;
		player.getSprite().setTexCoord(new Vector2f(0.0f, 168.0f));
		player.getSprite().setTexSize(new Vector2f(32.0f, 32.0f));
		cities = new City[5];
		selectionSprite.setCamPosition(player.getPosition());
		floorTile.setCamPosition(player.getPosition());
		shadowSprite.setCamPosition(player.getPosition());
		healthSprite.setPosition(new Vector2f(400.0f, 250.0f));
		terrainDetail.setCamPosition(player.getPosition());
		//player.setPosition(new Vector2f(-2000.0f, -2000));
		
		cities[0] = new City(new Vector2f(-2000.0f, -2000), 5, 5, Team.TINY_HUMAN, player.getPosition());
		cities[1] = new City(new Vector2f(-2000.0f, 2000.0f), 4, 4, Team.TINY_ALIEN, player.getPosition());
		cities[2] = new City(new Vector2f(2000.0f, -2000), 4, 4, Team.TINY_ALIEN, player.getPosition());
		cities[3] = new City(new Vector2f(2000.0f, 2000.0f), 5, 5, Team.TINY_ALIEN, player.getPosition());
		cities[4] = new City(new Vector2f(0.0f, 0.0f), 6, 6, Team.TINY_ALIEN, player.getPosition());
		
		entities = new Entity[1500];
		
		for (int i = 0; i < 1500; ++i) {
			entities[i] = new Soldier((i < 600) ? Team.TINY_HUMAN : Team.TINY_ALIEN);
			entities[i].setMoving(true);
			entities[i].setSpeed(60.0f);
			entities[i].getSprite().setWidth(16.0f);
			entities[i].getSprite().setHeight(16.0f);
			entities[i].setRadius(8.0f);
			entities[i].spawn(cities);
			entities[i].getSprite().setCamPosition(player.getPosition());
		}
		
		for (Entity entity : entities) {
			if (entity instanceof Soldier) {
				AI ai = new StandardAI(entity, entities, cities, bullets, player);
				((Soldier)entity).setAI(ai);
			}
		}
		
		for (int i = 0; i < 100; ++i) {
			detailState[i] = (int)(Math.random() * 3.0);
			detailPosition[i] = new Vector2f((float)Math.random() * 5000.0f - 2500.0f, (float)Math.random() * 5000.0f - 2500.0f);
		}
	}
	
	@Override
	public void handleInput() {
		player.setMoving(false);
		player.setSelecting(false);
		
		short direction = 0x0000;
		
		if (Input.checkPressedKey(Keyboard.KEY_A)) {
			direction |= 0x0001;
		}
		if (Input.checkPressedKey(Keyboard.KEY_D)) {
			direction |= 0x0010;
		}
		if (Input.checkPressedKey(Keyboard.KEY_W)) {
			direction |= 0x0100;
		}
		if (Input.checkPressedKey(Keyboard.KEY_S)) {
			direction |= 0x1000;
		}
		
		switch(direction) {
		case 0x0001:
			player.setMoving(true);
			player.setTheta((float)Math.PI);
			break;
		case 0x0010:
			player.setMoving(true);
			player.setTheta(0.0f);
			break;
		case 0x0100:
			player.setMoving(true);
			player.setTheta(1.5f * (float)Math.PI);
			break;
		case 0x1000:
			player.setMoving(true);
			player.setTheta(0.5f * (float)Math.PI);
			break;
		case 0x0101:
			player.setMoving(true);
			player.setTheta(1.25f * (float)Math.PI);
			break;
		case 0x0110:
			player.setMoving(true);
			player.setTheta(1.75f * (float)Math.PI);
			break;
		case 0x1010:
			player.setMoving(true);
			player.setTheta(0.25f * (float)Math.PI);
			break;
		case 0x1001:
			player.setMoving(true);
			player.setTheta(0.75f * (float)Math.PI);
			break;
		default:
			player.setMoving(false);
		}
		
		if (Input.checkPressedMouseBtn(0)) {
			if (player.getTimer() > 0.1f) {
				createBullet(player, (float)(Math.PI + Math.atan2(300.0f - (float)Input.getMouseY(),
															 	  400.0f - (float)Input.getMouseX())));
				player.setTimer(0.0f);
			}
		}
		if (Input.checkPressedMouseBtn(1)) {
			player.increaseSelectRadius(100.0f * Timer.getLoopTime());
			player.setSelecting(true);
		}
		if (Input.checkReleasedMouseBtn(1)) {
			player.setFollowed(false);
			for (Entity entity : entities) {
				if (entity.getTeam() == player.getTeam() &&
					(float)Math.abs(entity.getPosition().x - player.getPosition().x) < player.getSelectRadius() &&
					(float)Math.abs(entity.getPosition().y - player.getPosition().y) < player.getSelectRadius()) {
					player.setFollowed(true);
					entity.setSelected(true);
				}
			}
			
			if (!player.isFollowed()) {
				for (Entity entity : entities) {
					entity.setSelected(false);
				}
			}
			
			player.setSelectRadius(0.0f);
		}
		
		if (Input.checkReleasedKey(Keyboard.KEY_ESCAPE)) {
			game.setGameState(game.getMenu());
		}
	}
	
	private void createBullet(Entity source, float aimTheta) {
		SoundSystem.playSound(Sound.LASER);
		Bullet bullet = new Bullet(source.getTeam());
		bullet.setSpeed(300.0f);
		bullet.getSprite().setTexCoord(new Vector2f(8.0f, 32.0f));
//		bullet.getSprite().setColor((source.getTeam() == Team.TINY_HUMAN) ? new Color(0.0f, 0.2f, 0.6f, 1.0f) : new Color(1.0f, 0.0f, 0.0f, 1.0f));
		bullet.getSprite().setColor(new Color(1.0f, 1.0f, 1.0f, 1.0f));
		bullet.getSprite().setSpriteState((source.getTeam() == Team.TINY_HUMAN) ? 0 : 1);
		bullet.setPosition(new Vector2f(source.getPosition().x + 40.0f * (float)Math.cos(aimTheta),
										source.getPosition().y + 40.0f * (float)Math.sin(aimTheta)));
		bullet.setTheta(aimTheta);
		bullet.getSprite().setCamPosition(player.getPosition());
		bullet.setMoving(true);
		bullets.add(bullet);
	}

	@Override
	public void calcConfig() {
		for (Entity entity : entities) {
			entity.sync(cities);
			player.handleCollisions(entity);
		}
		
		for (City city : cities) {
			city.sync(entities);
			city.handleCollisions(player);
		}
		
		player.sync(cities);
		
		for (Iterator<Bullet> bulletI = bullets.iterator(); bulletI.hasNext();) {
			Bullet bullet = bulletI.next();
			if (bullet.getTimer() > 2.0f || !bullet.isEnabled()) {
				bulletI.remove();
			}
		}
		
		for (Bullet bullet : bullets) {
			bullet.sync(cities);
			bullet.handlePlayerCollisions(player);
			for (Entity entity : entities) {
				bullet.handleCollisions(entity);
			}
			for (City city : cities) {
				for (Building building : city.getBuildings()) {
					bullet.handleBuildingCollisions(building);
				}
			}
		}
		
		checkVictory();
	}

	private void checkVictory() {
		Victory.humanCities = 0;
		Victory.alienCities = 0;
		for (City city : cities) {
			if (!city.isContested()) {
				if (city.getTeam() == Team.TINY_HUMAN) {
					++Victory.humanCities;
				} else {
					++Victory.alienCities;
				}
			}
		}
		
		if (Victory.alienCities == 5) {
			game.setGameState(game.getDefeat());
		} else if (Victory.humanCities == 5) {
			game.setGameState(game.getVictory());
		}
		
		if (player.getHealth() <= 0) {
			game.setGameState(game.getDeath());
		}
	}
	
	@Override
	public void render() {
		renderFloor();
		
		for (int i = 0; i < 100; ++i) {
			terrainDetail.setSpriteState(detailState[i]);
			terrainDetail.setPosition(detailPosition[i]);
			terrainDetail.render();
		}
		
		for (City city : cities) {
			city.render();
		}
		
		for (Entity entity : entities) {
			if (entity.isEnabled()) {
				shadowSprite.setPosition(new Vector2f(entity.getPosition().x, entity.getPosition().y + 8.0f));
				shadowSprite.render();
				entity.render();
			}
		}
		
		shadowSprite.setPosition(new Vector2f(player.getPosition().x, player.getPosition().y + 32.0f));
		
		shadowSprite.setWidth(64.0f);
		shadowSprite.setHeight(32.0f);
		shadowSprite.render();
		shadowSprite.setWidth(16.0f);
		shadowSprite.setHeight(16.0f);
		
		for (Entity bullet : bullets) {
			bullet.render();
		}
		
		if (player.isEnabled()) {
			int notMoving = 0;
			if (!player.isMoving()) {
				notMoving = 4;
			}
			
			float theta = (float)(Math.PI + Math.atan2(300.0f - (float)Input.getMouseY(),
				 	  								   400.0f - (float)Input.getMouseX()));
			player.getSprite().setSpriteState((int)((theta + 0.25f * (float)Math.PI) / (0.5f * (float)Math.PI) % 4) + notMoving);
			player.render();
		}
		
		renderGUI();
	}
	
	private void renderFloor() {
		for (int i = -1; i < 14; ++i) {
			for (int j = -1; j < 14; ++j) {
				floorTile.setPosition(new Vector2f(player.getPosition().x - 400.0f + i * 64.0f - (player.getPosition().x % 64.0f),
												   player.getPosition().y - 300.0f + j * 64.0f - (player.getPosition().y % 64.0f)));
				floorTile.render();
			}
		}
	}
	
	private void renderGUI() {
		selectionSprite.setPosition(player.getPosition());
		selectionSprite.setWidth(2.0f * player.getSelectRadius());
		selectionSprite.setHeight(2.0f * player.getSelectRadius());
		selectionSprite.render();
		
		healthSprite.setWidth(64.0f * (player.getHealth() / 1000.0f));
		healthSprite.render();
		
		minimapBG.render();
		minimapToken.setWidth(8.0f);
		minimapToken.setHeight(8.0f);
		
		for (City city : cities) {
			minimapToken.setPosition(new Vector2f(686.0f + (city.getPosition().x + 2500.0f) * 0.02f, 486.0f + (city.getPosition().y + 2500.0f) * 0.02f));
			if (city.isContested()) {
				minimapToken.setColor(new Color(1.0f, 0.5f, 0.0f, 1.0f));
			} else {
				minimapToken.setColor((city.getTeam() == Team.TINY_HUMAN) ? new Color(0.0f, 0.0f, 1.0f, 1.0f) : new Color(1.0f, 0.0f, 0.0f, 1.0f));
			}
			minimapToken.render();
		}
		
		minimapToken.setPosition(new Vector2f(686.0f + (player.getPosition().x + 2500.0f) * 0.02f, 486.0f + (player.getPosition().y + 2500.0f) * 0.02f));
		minimapToken.setColor(new Color(0.0f, 0.5f, 0.0f, 1.0f));
		minimapToken.setWidth(4.0f);
		minimapToken.setHeight(4.0f);
		minimapToken.render();
	}
}
